package com.bruce.tool.mail.core;

import com.bruce.tool.common.util.valid.ValidUtils;
import com.bruce.tool.mail.auth.AuthAdapter;
import com.bruce.tool.mail.constant.Constants;
import com.bruce.tool.mail.exception.MailException;
import com.bruce.tool.office.excel.util.ContentUtils;
import com.bruce.tool.office.excel.util.WorkBookUtils;
import com.google.common.io.Files;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.util.CollectionUtils;

import javax.mail.Authenticator;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import javax.validation.constraints.NotNull;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

/**
 * 功能 :
 *
 * @author : Bruce(刘正航) 6:41 PM 2018/12/8
 */
@Slf4j
@NoArgsConstructor(staticName = "create")
public class Mail {

    /**
     * 发件人邮箱服务器
     */
    @NotNull(message = "邮箱服务器不能为空")
    private String host = "";
    /**
     * 发件人用户名
     */
    @NotNull(message = "邮箱用户名不能为空")
    private String username = "";
    /**
     * 发件人密码
     */
    @NotNull(message = "邮箱密码不能为空")
    private String password = "";
    /**
     * 发件人邮箱
     */
    @NotNull(message = "发件人不能为空")
    private String from = "";
    /**
     * 收件人邮箱，多个邮箱以“;”分隔
     */
    @NotNull(message = "收件人不能为空")
    private String to = "";
    /**
     * 抄送人邮箱，多个邮箱以“;”分隔
     */
    private String copy = "";
    /**
     * 密送人邮箱，多个邮箱以“;”分隔
     */
    private String secret = "";
    /**
     * 邮件主题
     */
    @NotNull(message = "邮件主题不能为空")
    private String subject = "";
    /**
     * 邮件内容
     */
    @NotNull(message = "邮件内容不能为空")
    private String content = "";

    /**
     * 邮件中的图片，为空时无图片。map中的key为图片ID，value为图片地址
     */
    private Map<String, String> pictures = new HashMap<>(0);
    /**
     * 邮件中的附件，为空时无附件。map中的key为附件ID，value为附件地址
     */
    private Map<String, String> attachments = new HashMap<>(0);
    ;
    /**
     * 邮件中的附件，为空时无附件。map中的key为附件ID，value为附件字节数组
     */
    private Map<String, byte[]> byteAttachments = new HashMap<>(0);

    /**
     * 是否追加附件内容到邮件内容中:目前只支持excel2003版本
     **/
    private boolean appendToContent;

    /**
     * 认证信息
     **/
    public Mail auth(String username, String password) {
        // 用户名就是发件人
        this.from = username;
        this.username = username;
        this.password = password;
        return this;
    }

    /**
     * 邮件服务器
     **/
    public Mail host(String host) {
        this.host = host;
        return this;
    }

    /**
     * 收件人
     **/
    public Mail to(String to) {
        this.to = to;
        return this;
    }

    /**
     * 抄送人
     **/
    public Mail copy(String copy) {
        this.copy = copy;
        return this;
    }

    /**
     * 密送人
     **/
    public Mail secret(String secret) {
        this.secret = secret;
        return this;
    }

    /**
     * 邮件主题
     **/
    public Mail subject(String subject) {
        this.subject = subject;
        return this;
    }

    /**
     * 邮件内容
     **/
    public Mail content(String content) {
        this.content = content;
        return this;
    }

    public Mail pictures(Map<String, String> pictures) {
        if (CollectionUtils.isEmpty(pictures)) {
            return this;
        }
        this.pictures = pictures;
        return this;
    }

    public Mail picture(String pid, String url) {
        if (StringUtils.isBlank(pid) || StringUtils.isBlank(url)) {
            return this;
        }
        this.pictures.put(pid, url);
        return this;
    }

    public Mail attachments(Map<String, String> attachments) {
        if (CollectionUtils.isEmpty(attachments)) {
            return this;
        }
        this.attachments = attachments;
        return this;
    }

    public Mail attachment(String pid, String url) {
        if (StringUtils.isBlank(pid) || StringUtils.isBlank(url)) {
            return this;
        }
        this.attachments.put(pid, url);
        return this;
    }

    public Mail byteAttachments(Map<String, byte[]> byteAttachments) {
        if (CollectionUtils.isEmpty(byteAttachments)) {
            return this;
        }
        this.byteAttachments = byteAttachments;
        return this;
    }

    public Mail byteAttachment(String pid, byte[] bytes) {
        if (StringUtils.isBlank(pid) || Objects.isNull(bytes)) {
            return this;
        }
        this.byteAttachments.put(pid, bytes);
        return this;
    }

    public Mail appendToContent(boolean appendToContent) {
        this.appendToContent = appendToContent;
        return this;
    }

    /**
     * 发送邮件
     */
    public void send() {

        ValidUtils.valid(this);

        // 附件名称太长,需要设置此属性,否则会是乱码
        System.setProperty("mail.mime.splitlongparameters", "false");

        JavaMailSenderImpl senderImpl = new JavaMailSenderImpl();
        // 设定mail server
        senderImpl.setHost(this.host);
        // 建立邮件消息
        MimeMessage mailMessage = senderImpl.createMimeMessage();

        try {
            MimeMessageHelper messageHelper = new MimeMessageHelper(mailMessage, true, "UTF-8");

            // 添加邮件校验信息
            addSessionInfo(senderImpl);

            // 设置发件人邮箱
            messageHelper.setFrom(this.from);

            // 设置收件人邮箱
            addToEmails(messageHelper);
            // 设置抄送人邮箱
            addCopyEmails(messageHelper);
            // 设置密送人邮箱
            addSecretEmails(messageHelper);

            // 邮件主题
            messageHelper.setSubject(this.subject);
            // 添加图片
            addPictures(messageHelper);
            // 添加附件
            addAttachments(messageHelper);
            // 添加字节码附件
            addByteAttachments(messageHelper);
            // 追加附件内容到邮件正文
            addContent(messageHelper);
        } catch (MessagingException e) {
            throw new MailException(e);
        }

        // 发送邮件
        senderImpl.send(mailMessage);
    }

    /**
     * 邮箱登录
     **/
    private void addSessionInfo(JavaMailSenderImpl senderImpl) {
        Properties prop = new Properties();
        // 将这个参数设为true，让服务器进行认证,认证用户名和密码是否正确
        prop.setProperty("mail.smtp.auth", "true");
        prop.setProperty("mail.smtp.timeout", "25000");

        prop.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        prop.setProperty("mail.smtp.socketFactory.fallback", "false");
        prop.setProperty("mail.smtp.socketFactory.port", "465");
        prop.setProperty("mail.smtp.port", "456");

        // 添加验证
        Authenticator auth = new AuthAdapter(this.username, this.password);
        Session session = Session.getDefaultInstance(prop, auth);
        senderImpl.setSession(session);
    }

    /**
     * 添加附件-byte数组方式
     **/
    private void addByteAttachments(MimeMessageHelper messageHelper) throws MessagingException {
        if (Objects.isNull(byteAttachments)) {
            return;
        }
        for (Map.Entry<String, byte[]> entry : byteAttachments.entrySet()) {
            String cid = entry.getKey();
            byte[] byteArray = entry.getValue();
            if (null == cid || null == byteArray) {
                throw new MailException("请确认每个附件的ID和内容是否齐全！");
            }
            if (byteArray.length == 0) {
                throw new MailException("附件" + cid + "不存在！");
            }
            ByteArrayResource byteResource = new ByteArrayResource(byteArray);
            messageHelper.addAttachment(cid, byteResource);
        }
    }

    /**
     * 添加附件-文件方式
     **/
    private void addAttachments(MimeMessageHelper messageHelper) throws MessagingException {
        if (Objects.isNull(attachments)) {
            return;
        }
        for (Map.Entry<String, String> entry : attachments.entrySet()) {
            String cid = entry.getKey();
            String filePath = entry.getValue();
            if (null == cid || null == filePath) {
                throw new MailException("请确认每个附件的ID和地址是否齐全！");
            }
            File file = new File(filePath);
            if (!file.exists()) {
                throw new MailException("附件" + filePath + "不存在！");
            }

            String suffix = Files.getFileExtension(filePath);

            if (StringUtils.isNotBlank(suffix)) {
                cid = cid + "." + suffix;
            }

            FileSystemResource fileResource = new FileSystemResource(file);

            if ("xlsx".equals(suffix)) {
                messageHelper.addAttachment(cid, fileResource, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                return;
            }
            messageHelper.addAttachment(cid, fileResource);
        }
    }

    /**
     * 添加附件-图片路径方式
     **/
    private void addPictures(MimeMessageHelper messageHelper) throws MessagingException {
        if (Objects.isNull(pictures)) {
            return;
        }
        for (Map.Entry<String, String> entry : pictures.entrySet()) {
            String cid = entry.getKey();
            String filePath = entry.getValue();
            if (null == cid || null == filePath) {
                throw new MailException("请确认每张图片的ID和图片地址是否齐全！");
            }
            File file = new File(filePath);
            if (!file.exists()) {
                throw new MailException("图片" + filePath + "不存在！");
            }
            FileSystemResource img = new FileSystemResource(file);
            messageHelper.addInline(cid, img);
        }
    }

    /**
     * 添加收件人
     **/
    private void addToEmails(MimeMessageHelper messageHelper) throws MessagingException {
        String[] toEmailArray = StringUtils.split(this.to, Constants.SPLIT_SEPARATOR);
        if (toEmailArray.length == 0) {
            throw new MailException("收件人邮箱不得为空！");
        }
        messageHelper.setTo(toEmailArray);
    }

    /**
     * 添加抄送人
     **/
    private void addCopyEmails(MimeMessageHelper messageHelper) throws MessagingException {
        String[] copyEmailArray = StringUtils.split(this.copy, Constants.SPLIT_SEPARATOR);
        if (copyEmailArray.length == 0) {
            return;
        }
        messageHelper.setCc(copyEmailArray);
    }

    /**
     * 添加密送人
     **/
    private void addSecretEmails(MimeMessageHelper messageHelper) throws MessagingException {
        String[] secretEmailArray = StringUtils.split(this.secret, Constants.SPLIT_SEPARATOR);
        if (secretEmailArray.length == 0) {
            return;
        }
        messageHelper.setBcc(secretEmailArray);
    }

    /**
     * 设置邮件正文
     **/
    private void addContent(MimeMessageHelper messageHelper) {
        if (this.appendToContent) {
            StringBuilder builder = new StringBuilder();
            // 文件类型的附件
            for (Map.Entry<String, String> entry : this.attachments.entrySet()) {
                try (InputStream in = new FileInputStream(new File(entry.getValue()))) {
                    appendToContent(builder, in);
                } catch (IOException e) {
                    throw new MailException(e);
                }
            }
            // 字节流类型的附件
            for (Map.Entry<String, byte[]> entry : this.byteAttachments.entrySet()) {
                InputStream in = new ByteArrayInputStream(entry.getValue());
                appendToContent(builder, in);
            }
            if (StringUtils.isNotBlank(builder.toString())) {
                this.content += builder.toString();
            }
        }

        try {
            messageHelper.setText(this.content, true);
        } catch (MessagingException e) {
            throw new MailException(e);
        }
    }

    /**
     * 指定流数据-追加到邮件主体内容中
     **/
    private void appendToContent(StringBuilder content, InputStream in) {
        if (Objects.isNull(in)) {
            return;
        }
        // 不是excel,就不走内容追加逻辑
        if (!WorkBookUtils.isExcel(in)) {
            return;
        }
        try {
            String tableContent = ContentUtils.setTableWidth(ContentUtils.transfer(in));
            if (null == tableContent || tableContent.trim().length() == 0) {
                return;
            }
            if (StringUtils.isNotBlank(tableContent)) {
                content.append("<br/>");
                content.append(tableContent);
                content.append("<br/>");
            }
        } catch (ParserConfigurationException | TransformerException e) {
            throw new MailException(e);
        }
    }

}
